/**
 * 取数组中的最大值
 * @type {function(Array, Function): *}
 * @param 第一个参数（必选）目标数组
 * @param 第二个参数（可选）选取目标数组中的个体的某个值来做比较
 */

const _max = makeCompareFunction((a, b) => {
    return a < b;
});

_max([{age: 18}, {age: 20}, {age: 21}], (obj) => {
    return obj.age;
});

/**
 * 取数组中的最小值
 * @type {function(Array, Function): *}
 * @param 第一个参数（必选）目标数组
 * @param 第二个参数（可选）选取目标数组中的个体的某个值来做比较
 */
const _min = makeCompareFunction((a, b) => {
    return a > b;
});

_min([{age: 18}, {age: 20}, {age: 21}], (obj) => {
    return obj.age;
});

/**
 * 比较函数的工厂函数
 * @param bestFn（a,b） 如果a比b更好返回true，否则返回false
 */
function makeCompareFunction(bestFn) {
    return function (arr, valueFn = n => n) {
        arr.reduce((pre, cur) => {
            const bestValue = valueFn(pre);
            const curValue = valueFn(cur);

            return bestFn(bestValue, curValue) ? pre : cur;
        });

        return _max;
    }
}

const min = makeBestFunction((a, b) => a > b);
const max = makeBestFunction((a, b) => a < b);

function best(arr, fn) {
    return arr.reduce((pre, cur) => {
        return fn(pre, cur) ? pre : cur;
    });
}

function makeBestFunction(fn) {
    return (arr) => {
        return best(arr, fn);
    };
}

function repeatedly(times, fn) {
    return new Array(times).fill(undefined).map(fn);
}

repeatedly(3, () => {
    return Math.floor(Math.random() * 10 + 1);
});

function repeatedUtil(fn, check, init) {
    const ret = [];
    let result = fn(init);

    while (check(result)) {
        ret.push(result);
        result = fn(result);
    }

    return ret;
}

repeatedUtil(n => n + n, n => n <= 1024, 1)

function uniqueString(prefix) {
    return [prefix, new Date().getTime()].join('');
}

function makeUniqueStringFunction(start) {
    let count = start;
    return (prefix) => {
        return [prefix, count++].join('');
    };
}

const omgenerator = (function (init) {
    let counter = init;

    return {
        uniqueString: (prefix) => {
            return [prefix, counter++].join('');
        }
    }
})(0);

function fNull(fn, ...defaults) {
    return (...args) => {
        args = args.map((val, i) => {
            return existence(val) ? val : defaults[i];
        })

        fn(...args);
    };
}

function existence(n) {
    return n !== false && n != null;
}

function checker(...validators) {
    return (obj) => {
        return validators.reduce((errs, check) => {
            if (!check(obj)) {
                errs.push(check.message);
            }
            return errs;
        }, []);
    };
}

function validator(message, fn) {
    const f = (...args) => {
        return fn(...args);
    };
    f['message'] = message;

    return f;
}

function always(val) {
    return () => {
        return val;
    };
}

let gonnaFail = checker(validator("first err", always(false)));
gonnaFail({});

function isObj(obj) {
    return (obj instanceof Object);
}

let checkCommand = checker(validator("must be a map", isObj));
checkCommand(1);

function exist(obj) {
    return obj != null;
}

function hasKeys(...keys) {
    const f = (obj) => {
        return keys.every(key => {
            return exist(obj?.key);
        });
    };

    return validator(['Must have values for keys:', keys].join(' '), f);
}

